from typing import Dict, Any, Tuple, Optional, List
import asyncio
import json
import uuid
from agent.check import chat as agent_chat, check as agent_check
from fastapi import HTTPException, status
from schemas.agent import CheckRequest, AgentCheckResponse
from schemas.issue import IssueCreate
from crud.issue import create_issue
from db.session import AsyncSessionLocal
from log import log_error, log_info


async def handle_chat(user_input: str, conversation_history: Optional[List[Dict[str, Any]]] = None) -> Tuple[str, Dict[str, Any]]:
    """
    Handle chat (free conversation) with the agent.
    
    Args:
        user_input: The user's input message
        conversation_history: Previous conversation history
        
    Returns:
        Tuple containing the agent's response and the complete shared context
        
    Raises:
        HTTPException: If an error occurs during processing
    """
    try:
        result, shared = await asyncio.to_thread(agent_chat, user_input, conversation_history)
        return result, shared
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error processing chat request: {str(e)}"
        )


async def handle_check(user_input: str) -> Tuple[str, Dict[str, Any]]:
    """
    Handle check (correction) mode with the agent.
    
    Args:
        user_input: The user's input JSON string
        
    Returns:
        Tuple containing the agent's response and the complete shared context
        
    Raises:
        HTTPException: If an error occurs during processing or input is invalid
    """
    try:
        result, shared = await asyncio.to_thread(agent_check, user_input)
        return result, shared
    except ValueError as e:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"Invalid input format: {str(e)}"
        )
    except Exception as e:
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Error processing check request: {str(e)}"
        )


async def prepare_check_requests(requests: List[CheckRequest]) -> List[CheckRequest]:
    """
    预处理检查请求列表，过滤无效请求并准备数据。
    
    Args:
        requests: 原始检查请求列表
        
    Returns:
        过滤后的有效请求列表
        
    Raises:
        HTTPException: 如果没有有效的请求
    """
    if not requests:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="请求列表不能为空"
        )
    
    prepared_requests = []
    for item in requests:
        # 过滤掉无效的请求（没有检查项或描述为空）
        if not item.inspector:
            continue
            
        # 确保每条请求都有 id
        if item.id is None:
            item.id = str(uuid.uuid4())
            
        # 验证 plan_id 是否存在
        if item.plan_id is None or item.plan_id == "":
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="plan_id 不能为空"
            )
            
        prepared_requests.append(item)
    
    return prepared_requests


async def process_check_response(cr: CheckRequest, result_str: str) -> List[AgentCheckResponse]:
    """
    处理单个检查请求的响应，解析结果并保存到数据库。
    
    Args:
        cr: 原始检查请求
        result_str: 大模型返回的结果字符串
        
    Returns:
        解析后的响应列表
    """
    try:
        result_dict = json.loads(result_str) if isinstance(result_str, str) else result_str
        
        # 检查响应是否包含 'issues' 键且为列表
        if not isinstance(result_dict.get('issues'), list):
            raise ValueError("Response does not contain 'issues' list")
            
        responses = []
        for issue in result_dict['issues']:
            # 使用大模型返回的每个issue字段构建AgentResponse
            response = AgentCheckResponse(
                id=str(uuid.uuid4()), 
                item_id=cr.item_id,
                plan_id=cr.plan_id,
                type=issue.get("type"),
                severity=issue.get("severity"),
                description=issue.get("description"),
                suggestion=issue.get("suggestion"),
                title=issue.get("title"),
                originalContent=issue.get("originalContent"),
                originalDescription=cr.description
            )
            responses.append(response)
            
            # 保存问题到数据库
            try:
                issue_data = IssueCreate(
                    id=response.id,
                    item_id=response.item_id,
                    plan_id=response.plan_id,
                    type=response.type,
                    severity=response.severity,
                    title=response.title,
                    description=response.description,
                    suggestion=response.suggestion,
                    original_content=response.originalContent,
                    original_description=response.originalDescription
                )
                # 使用独立会话从连接池获取连接
                async with AsyncSessionLocal() as session:
                    await create_issue(db=session, issue=issue_data)
            except Exception as e:
                # 记录错误但不影响请求
                log_error(f"Failed to save issue to database: {str(e)}")

        return responses
        
    except (json.JSONDecodeError, ValueError) as e:
        # 如果解析失败或格式无效，返回空列表
        return []


async def batch_check_requests(requests: List[CheckRequest]) -> List[AgentCheckResponse]:
    """
    批量处理检查请求，支持并发调用和结果聚合。
    
    Args:
        requests: 检查请求列表
        
    Returns:
        所有检查结果的聚合列表
    """
    # 预处理请求
    prepared_requests = await prepare_check_requests(requests)
    
    # 控制并发量，防止过载或超出速率限制
    # 设置为15以配合线程池的最大线程数
    sem = asyncio.Semaphore(15)

    async def _process(cr: CheckRequest) -> List[AgentCheckResponse]:
        async with sem:
            user_input = json.dumps(cr.model_dump())
            log_info("====并行开启大模型接口调用====")
            # handle_check 已经是异步函数，直接调用
            result_str, _shared = await handle_check(user_input)
            
            # 处理响应并保存到数据库
            return await process_check_response(cr, result_str)

    # 使用 asyncio.gather 保持输入顺序
    results = await asyncio.gather(*[_process(cr) for cr in prepared_requests])

    # 展平结果列表
    return [item for sublist in results for item in sublist]